package com.beimin.evedata.hibernate;

import java.util.Collection;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Distinct;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

import com.beimin.evedata.model.inv.InvBlueprint;
import com.beimin.evedata.model.inv.InvCategory;
import com.beimin.evedata.model.inv.InvControlTowerResource;
import com.beimin.evedata.model.inv.InvFlag;
import com.beimin.evedata.model.inv.InvGroup;
import com.beimin.evedata.model.inv.InvMarketGroup;
import com.beimin.evedata.model.inv.InvMetaType;
import com.beimin.evedata.model.inv.InvType;
import com.beimin.evedata.repository.InvRepository;
import java.util.HashSet;
import java.util.List;

public class HibInvRepository implements InvRepository {
	private final SessionFactory sf;

	public HibInvRepository(SessionFactory sf) {
		this.sf = sf;
	}

	@Override
	public InvBlueprint getBlueprint(int blueprintID) {
		return (InvBlueprint) sf.getCurrentSession().get(InvBlueprint.class, blueprintID);
	}

	@Override
	public InvCategory getCategory(int typeID) {
		return (InvCategory) sf.getCurrentSession().get(InvCategory.class, typeID);
	}

	@Override
	@SuppressWarnings("unchecked")
	public Collection<InvControlTowerResource> getControlTowerResources(int controlTowerTypeID) {
		Session session = sf.getCurrentSession();
		Criteria criteria = session.createCriteria(InvControlTowerResource.class);
		criteria.add(Restrictions.eq("controlTowerTypeID", controlTowerTypeID));
		return criteria.list();
	}

	@Override
	@SuppressWarnings("unchecked")
	public Collection<Integer> getControlTowerResourcesTypes() {
		Session session = sf.getCurrentSession();
		Criteria criteria = session.createCriteria(InvControlTowerResource.class);
		Projection projection = new Distinct(Projections.groupProperty("resourceTypeID"));
		criteria.setProjection(projection);
		return criteria.list();
	}

	@Override
	public InvControlTowerResource getControlTowerResource(int controlTowerTypeID, int resourceTypeID) {
		Session session = sf.getCurrentSession();
		Criteria criteria = session.createCriteria(InvControlTowerResource.class);
		criteria.add(Restrictions.eq("controlTowerTypeID", controlTowerTypeID));
		criteria.add(Restrictions.eq("resourceTypeID", resourceTypeID));
		return (InvControlTowerResource) criteria.uniqueResult();
	}

	@Override
	public InvFlag getFlag(int flagID) {
		return (InvFlag) sf.getCurrentSession().get(InvFlag.class, flagID);
	}

	@Override
	public InvGroup getGroup(int groupID) {
		return (InvGroup) sf.getCurrentSession().get(InvGroup.class, groupID);
	}

	@Override
	public InvMarketGroup getMarketGroup(int marketGroupID) {
		return (InvMarketGroup) sf.getCurrentSession().get(InvMarketGroup.class, marketGroupID);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Collection<InvMarketGroup> getMarketGroups(Integer... marketGroupIds) {
		Session session = sf.getCurrentSession();
		Criteria criteria = session.createCriteria(InvMarketGroup.class);
		criteria.add(Restrictions.in("marketGroupID", marketGroupIds));
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public Collection<InvMarketGroup> getChildMarketGroups(InvMarketGroup parentGroup) {
		Session session = sf.getCurrentSession();
		Criteria criteria = session.createCriteria(InvMarketGroup.class);
		criteria.add(Restrictions.eq("parentMarketGroup", parentGroup));
		return criteria.list();
	}

	@Override
	public InvType getType(int typeID) {
		return (InvType) sf.getCurrentSession().get(InvType.class, typeID);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Collection<InvGroup> getGroups(Integer[] groupIds) {
		Session session = sf.getCurrentSession();
		Criteria criteria = session.createCriteria(InvGroup.class);
		criteria.add(Restrictions.in("groupID", groupIds));
		return criteria.list();
	}
  
  @Override
  public Collection<InvType> getVariations(int invType) {
    Collection<InvType> types = new HashSet<InvType>();
    types.add(getType(invType));
    // if the parentType is the 'invType' then add the type.
    // if the type is the 'invType' then add the parentType.
    
    // TODO optimise with a queue - once an item has been looked up, it doesn't
    // need to be looked up again.
    
    int size;
    do {
      size = types.size();
      for (InvType type : types) {
        for (InvMetaType imt : (List<InvMetaType>)sf.getCurrentSession()
                .createCriteria(InvMetaType.class)
                .add(Restrictions.eq("type", type))
                .list()) {
          types.add(imt.getParentType());
        }

        for (InvMetaType imt : (List<InvMetaType>)sf.getCurrentSession()
                .createCriteria(InvMetaType.class)
                .add(Restrictions.eq("parentType", type))
                .list()) {
          types.add(imt.getType());
        }
      }
    } while (size != types.size());

    return types;
  }

  @Override
  public InvBlueprint getBlueprintForItem(int invType) {
		Session session = sf.getCurrentSession();
		Criteria criteria = session
            .createCriteria(InvBlueprint.class)
            .add(Restrictions.eq("productType.typeID", invType)
            );
    List<InvBlueprint> bps = criteria.list();
    if (bps.isEmpty()) {
      return null;
    } else {
      return bps.get(0);
    }
  }
  
  @Override
  public InvMetaType getMetaType(int invType) {
		Session session = sf.getCurrentSession();
		Criteria criteria = session
            .createCriteria(InvMetaType.class)
            .add(Restrictions.eq("type.typeID", invType)
            );
    return (InvMetaType)criteria.uniqueResult();
  }
}
